﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;
using System.ComponentModel;
using System.Collections;
using ProgNetComponents.Design.Attributes;
using ProgNetComponents.Controls;
using ProgNetComponents.Classes;

namespace ProgNetComponents.Data
{
    /// <summary>
    /// Base wrapper class for DB Operations
    /// </summary>
    public abstract class DataManager : BaseCustomComponent
    {
        /// <summary>
        /// List of tables that need AcceptChanges called
        /// </summary>
        protected List<DataTable> TransUpdate = new List<DataTable>();

        /// <summary>
        /// Creates DataManager instance
        /// </summary>
        protected DataManager()
        {
            UseGlobalConnectionString = true;
            AutoCommit = true;
        }

        /// <summary>
        /// Event fires when update single row failes in UpdateRows method when oneByOne is set to true
        /// </summary>
        public event ProgNetComponents.Utils.EventHandlers.DataRowEventHandler RowUpdateFailed;
        
        #region Fields
        private Dictionary<string, DbDataAdapter> adapters = new Dictionary<string, DbDataAdapter>();
        private DbDataAdapter fillAdapter = null;

        private SchemaChecker _SchemaChecker;
        private string _ConnectionString;
        private Exception _LastException;
        private DbTransaction transaction;

        private DbConnection _Connection;
        /// <summary>
        /// Gets connection for current connection string
        /// </summary>
        protected DbConnection Connection
        {
            get
            {
                if (_Connection == null)
                    _Connection = CreateConnection(ConnectionString);
                return _Connection;
            }
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets connection string.
        /// If UseGlobalConnectionString is set to true returns value of GetGlobalConnectionString() method
        /// </summary>
        [BrowsableByPropertyValue("UseGlobalConnectionString", false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string ConnectionString
        {
            get
            {
                if (UseGlobalConnectionString)
                    return GetGlobalConnectionString();
                return _ConnectionString;
            }
            set
            {
                if (adapters.Count != 0)
                    adapters.Clear();
                _ConnectionString = value;
                SchemaChecker.RefreshSchema();
            }
        }

        /// <summary>
        /// Gets last exception that occured
        /// </summary>
        [Browsable(false)]
        public Exception LastException
        {
            get { return _LastException; }
        }
        /// <summary>
        /// Gets or sets if GlobalConnectionString should be used
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool UseGlobalConnectionString
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets SchemaChecker for currend connection string
        /// </summary>
        [Browsable(false)]
        public SchemaChecker SchemaChecker
        {
            get
            {
                if (_SchemaChecker == null)
                {
                    _SchemaChecker = CreateSchemaChecker();
                }
                return _SchemaChecker;
            }
        }
        
        /// <summary>
        /// Gets count of tables added by AddTableDef
        /// </summary>
        [Browsable(false)]
        public int TableDefsCount
        {
            get { return adapters.Count; }
        }

        /// <summary>
        /// Gets or sets if Commit should be automaticly invoked during DataSet update
        /// </summary>
        public bool AutoCommit
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets if Exceptions should automaticly logged
        /// </summary>
        [DefaultValue(false)]
        public bool AutoLogExceptions
        {
            get;
            set;
        }
        #endregion

        #region Abstract
        /// <summary>
        /// Creates SchemaChecker
        /// </summary>
        /// <returns></returns>
        protected abstract SchemaChecker CreateSchemaChecker();
        /// <summary>
        /// Gets Global Connection String 
        /// </summary>
        /// <returns></returns>
        protected abstract string GetGlobalConnectionString();
        /// <summary>
        /// Creates DbCommandBuilder
        /// </summary>
        /// <param name="adapter"></param>
        /// <returns></returns>
        protected abstract DbCommandBuilder CreateCommandBuilder(DbDataAdapter adapter);
        /// <summary>
        /// Creates DbConnection
        /// </summary>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        protected abstract DbConnection CreateConnection(string connectionString);
        /// <summary>
        /// Creates DbDataAdapter
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="selectString"></param>
        /// <returns></returns>
        protected abstract DbDataAdapter CreateAdapter(DbConnection connection, string selectString);
        /// <summary>
        /// Creates DbParameter
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected abstract DbParameter CreateParameter(string name, object value);
        #endregion

        #region Table Definition
        /// <summary>
        /// Clears table definitions
        /// </summary>
        public void ClearTableDefs()
        {
            adapters.Clear();
        }
        /// <summary>
        /// Adds table definition. Update adapter will be created for this table
        /// </summary>
        /// <param name="dt">Table</param>
        public void AddTableDef(DataTable dt)
        {
            AddTableDef(dt, GetSelectString(dt, dt.TableName));
        }
        /// <summary>
        /// Adds table definition. Update adapter will be created for this table
        /// </summary>
        /// <param name="dt">Table</param>
        /// <param name="selectString">Select query</param>
        public void AddTableDef(DataTable dt, string selectString)
        {
            AddTableDef(dt, dt.TableName, selectString);
        }
        /// <summary>
        /// Adds table definition. Update adapter will be created for this table
        /// </summary>
        /// <param name="dt">Table</param>
        /// <param name="dbName">DB Table name</param>
        /// <param name="selectString">Select query</param>
        public void AddTableDef(DataTable dt, string dbName, string selectString)
        {
            if (!adapters.ContainsKey(dt.TableName))
            {
                DbDataAdapter ad = CreateAdapter(Connection, GetSelectString(dt, dbName));
                ad.AcceptChangesDuringUpdate = true;
                PrepareCommands(ad, dt, dbName);
                ad.SelectCommand.CommandText = selectString;
                adapters.Add(dt.TableName, ad);
            }
        }
        #endregion

        #region Execute/Update/Fill
        /// <summary>
        /// Executes the query and returns the first column of the first row in the result
        /// set returned by the query. All other columns and rows are ignored.
        /// </summary>
        /// <param name="query"></param>
        /// <param name="parameters"></param>
        /// <returns>The first column of the first row in the result set.</returns>
        public object ExecuteScalar(string query, params NamedObject[] parameters)
        {
            object obj = null;
            DbCommand cmd = null;
            DbConnection conn = null;
            _LastException = null;
            try
            {
                conn = CreateConnection(ConnectionString);
                cmd = conn.CreateCommand();
                cmd.CommandText = query;
                if (parameters != null && parameters.Length > 0)
                {
                    cmd.Parameters.Clear();
                    foreach (NamedObject kvp in parameters)
                    {
                        if (cmd.CommandText.Contains(kvp.Name))
                            cmd.Parameters.Add(CreateParameter(kvp.Name, kvp.Value));
                    }
                }

                conn.Open();
                obj = cmd.ExecuteScalar();
            }
            catch (Exception ex)
            {
                _LastException = ex;
                if (AutoLogExceptions)
                    ex.LogError();
            }
            finally
            {
                if (conn != null)
                {
                    if (conn.State != ConnectionState.Closed)
                        try
                        {
                            conn.Close();
                        }
                        catch { }
                    conn.Dispose();
                }
                if (cmd != null)
                    cmd.Dispose();
            }
            return obj;
        }
      
        /// <summary>
        /// Executes a SQL statement against a connection object.
        /// </summary>
        /// <param name="query"></param>
        /// <param name="parameters"></param>
        /// <returns>The number of rows affected.</returns>
        public int ExecuteNonQuery(string query, params NamedObject[] parameters)
        {
            int obj = -1;
            DbCommand cmd = null;
            DbConnection conn = null;
            _LastException = null;
            try
            {
                conn = CreateConnection(ConnectionString);
                cmd = conn.CreateCommand();
                cmd.CommandText = query;
                if (parameters != null && parameters.Length > 0)
                {
                    cmd.Parameters.Clear();
                    foreach (NamedObject kvp in parameters)
                    {
                        if (cmd.CommandText.Contains(kvp.Name))
                            cmd.Parameters.Add(CreateParameter(kvp.Name, kvp.Value));
                    }
                }
                conn.Open();
                obj = cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                _LastException = ex;
                if (AutoLogExceptions)
                    ex.LogError();
                else
                    ex.LogInternal();
            }
            finally
            {
                if (conn != null)
                {
                    if (conn.State != ConnectionState.Closed)
                        try
                        {
                            conn.Close();
                        }
                        catch { }
                    conn.Dispose();
                }
                if (cmd != null)
                    cmd.Dispose();
            }
            return obj;
        }

        /// <summary>
        /// Fills table using select statement and specified parameters
        /// </summary>
        /// <param name="dt">Table to fill</param>
        /// <param name="selectString">Select query</param>
        /// <param name="parameters">Parameters for select query</param>
        /// <returns></returns>
        public bool FillTable(DataTable dt, string selectString, params NamedObject[] parameters)
        {
            bool result = true;
            DbDataAdapter fillAdapter = CreateAdapter(CreateConnection(ConnectionString), selectString);
            try
            {
                if (parameters != null && parameters.Length > 0)
                {
                    fillAdapter.SelectCommand.Parameters.Clear();
                    foreach (NamedObject kvp in parameters)
                    {
                        if (fillAdapter.SelectCommand.CommandText.Contains(kvp.Name))
                            fillAdapter.SelectCommand.Parameters.Add(CreateParameter(kvp.Name, kvp.Value));
                    }
                }

                dt.Clear();
                fillAdapter.Fill(dt);
                dt.AcceptChanges();
            }
            catch (Exception ex)
            {
                result = false;
                _LastException = ex;
                if (AutoLogExceptions)
                    ex.LogError();
            }
            finally
            {
                fillAdapter.Dispose();
            }
            return result;
        }
      
        /// <summary>
        /// Fills table. Table needs to be registered with AddTableDef
        /// </summary>
        /// <param name="dt">Table</param>
        /// <param name="parameters">Parameters for select query</param>
        /// <returns></returns>
        public bool FillTable(DataTable dt, params NamedObject[] parameters)
        {
            bool result = true;
            DbDataAdapter ad = null;
            if (adapters.ContainsKey(dt.TableName))
            {
                ad = adapters[dt.TableName];
                lock (ad)
                {
                    try
                    {
                        if (parameters != null && parameters.Length > 0)
                        {
                            ad.SelectCommand.Parameters.Clear();
                            foreach (NamedObject kvp in parameters)
                            {
                                if (ad.SelectCommand.CommandText.Contains(kvp.Name))
                                    ad.SelectCommand.Parameters.Add(CreateParameter(kvp.Name, kvp.Value));
                            }
                        }
                        dt.Clear();
                        ad.Fill(dt);
                        dt.AcceptChanges();
                    }
                    catch (Exception ex)
                    {
                        result = false;
                        _LastException = ex;
                        if (AutoLogExceptions)
                            ex.LogError();
                    }
                }
            }
            return result;
        }
        
        /// <summary>
        /// Fills any table in dataset. Table need to be added using AddTableDef
        /// </summary>
        /// <param name="ds">DataSet</param>
        /// <param name="parameters">Parameters for all updates. Parameters should have unique names in each table</param>
        /// <returns></returns>
        public bool FillDataSet(DataSet ds, params NamedObject[] parameters)
        {
            bool result = true;
            ds.EnforceConstraints = false;
            foreach (DataTable dt in ds.Tables)
            {
                result &= FillTable(dt, parameters);
                if (!result)
                    break;
            }
            try
            {
                ds.EnforceConstraints = true;
            }
            catch (Exception ex)
            {
                _LastException = ex;
                if (AutoLogExceptions)
                    ex.LogError();
                result = false;
            }
            return result;
        }

        /// <summary>
        /// Updates rows
        /// </summary>
        /// <param name="oneByOne">If true Update statement will be called for each row separately</param>
        /// <param name="rows">Array of rows</param>
        /// <returns></returns>
        public RowsUpdateResults UpdateRows(bool oneByOne, params DataRow[] rows)
        {
            RowsUpdateResults result = RowsUpdateResults.AllSucceeded;
            DbDataAdapter ad = null;
            bool dispose = false;
            if (rows == null || rows.Length == 0)
                return RowsUpdateResults.AllSucceeded;
            try
            {
                DataTable dt = rows[0].Table;
                if (adapters.ContainsKey(dt.TableName))
                {
                    ad = adapters[dt.TableName];
                }
                else
                {
                    ad = CreateAdapter(Connection, GetSelectString(dt, dt.TableName));
                    if (ad.UpdateCommand == null)
                    {
                        PrepareCommands(ad, dt, dt.TableName);
                    }
                    dispose = true;
                }

                if (ad != null)
                {
                    ad.UpdateCommand.Transaction = transaction;
                    ad.InsertCommand.Transaction = transaction;
                    ad.DeleteCommand.Transaction = transaction;
                    ad.SelectCommand.Transaction = transaction;
                    ad.AcceptChangesDuringUpdate = true;
                    if (oneByOne)
                    {
                        bool anyOk = false;
                        foreach (DataRow r in rows)
                        {
                            try
                            {
                                if (r.RowState != DataRowState.Unchanged)
                                {
                                    ad.Update(new DataRow[] { r });
                                    anyOk = true;
                                }
                            }
                            catch (Exception ex)
                            {
                                _LastException = ex;
                                if (AutoLogExceptions)
                                    ex.LogError();
                                result = RowsUpdateResults.NotAllSucceeded;
                                if (RowUpdateFailed != null)
                                    RowUpdateFailed(this, new Utils.EventHandlers.DataRowEventArgs(r));
                            }
                        }
                        if (!anyOk)
                            result = RowsUpdateResults.AllFailed;
                    }
                    else
                    {
                        ad.Update(rows);
                    }
                }
            }
            catch (Exception ex)
            {
                _LastException = ex;
                if (AutoLogExceptions)
                    ex.LogError();
                result = RowsUpdateResults.AllFailed;
            }
            finally
            {
                if (dispose)
                    ad.Dispose();
            }
            return result;
        }

        /// <summary>
        /// Updates table
        /// </summary>
        /// <param name="dt">Table</param>
        /// <returns></returns>
        public bool UpdateTable(DataTable dt)
        {
            bool result = true;
            DbDataAdapter ad = null;
            bool dispose = false;
            try
            {
                if (dt.GetChanges() == null)
                    return true;
                if (adapters.ContainsKey(dt.TableName))
                {
                    ad = adapters[dt.TableName];
                }
                else
                {
                    ad = CreateAdapter(Connection, GetSelectString(dt, dt.TableName));
                    if (ad.UpdateCommand == null)
                    {
                        PrepareCommands(ad, dt, dt.TableName);
                    }
                    dispose = true;
                }

                if (ad != null)
                {
                    ad.UpdateCommand.Transaction = transaction;
                    ad.InsertCommand.Transaction = transaction;
                    ad.DeleteCommand.Transaction = transaction;
                    ad.SelectCommand.Transaction = transaction;
                   
                    ad.AcceptChangesDuringUpdate = transaction == null;
                    if (transaction != null)
                        TransUpdate.Add(dt);
                    ad.Update(dt);
                }
            }
            catch (Exception ex)
            {
                _LastException = ex;
                if (AutoLogExceptions)
                    ex.LogError();
                result = false;
            }
            finally
            {
                if (dispose)
                    ad.Dispose();
            }
            return result;
        }
        
        /// <summary>
        /// Starts transaction if it is not started and updates table
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public bool UpdateTableInTransaction(DataTable dt)
        {
            StartTransaction();
            return UpdateTable(dt);
        }
        
        /// <summary>
        /// Updates all tables in DataSet
        /// </summary>
        /// <param name="ds"></param>
        /// <returns></returns>
        public bool UpdateDataSet(DataSet ds)
        {
            bool result = true;

            foreach (string tab in adapters.Keys)
            {
                if (ds.Tables.Contains(tab))
                {
                    DataTable dt = ds.Tables[tab];
                    result &= UpdateTableInTransaction(dt);
                    if (!result)
                        break;
                    else
                    {
                        foreach (DataRelation rel in ds.Relations)
                        {
                            if (rel.ParentTable == dt)
                            {
                                UpdateRelations(rel);
                            }
                        }
                    }
                }
            }
            if (AutoCommit)
            {
                if (result)
                    Commit();
                else
                    Rollback();
            }
            return result;
        }
        #endregion
        
        #region Transaction
        /// <summary>
        /// Starts Transaction
        /// </summary>
        public void StartTransaction()
        {
            if (Connection.State != ConnectionState.Open)
                Connection.Open();
            if (transaction == null)
            {
                transaction = Connection.BeginTransaction(IsolationLevel.RepeatableRead);
                TransUpdate.Clear();
            }
        }
       
        /// <summary>
        /// Commits transaction
        /// </summary>
        public void Commit()
        {
            if (transaction != null)
            {
                if (TransUpdate.Count > 0)
                {
                    foreach (DataTable table in TransUpdate)
                        table.AcceptChanges();
                }
                TransUpdate.Clear();

                transaction.Commit();
                Connection.Close();
                transaction.Dispose();
                transaction = null;
            }
        }
        
        /// <summary>
        /// Rollbacks transaction
        /// </summary>
        public void Rollback()
        {
            if (transaction != null)
            {
                TransUpdate.Clear();

                transaction.Rollback();
                Connection.Close();
                transaction.Dispose();
                transaction = null;
            }
        }
        #endregion

        /// <summary>
        /// Gets Select query for specified table
        /// </summary>
        /// <param name="dt">Table</param>
        /// <param name="tableName">DB Table name</param>
        /// <returns></returns>
        public string GetSelectString(DataTable dt, string tableName)
        {
            string str = "SELECT ";
            string cols = "";
            foreach (DataColumn col in dt.Columns)
            {
                if (SchemaChecker.ColumnExists(tableName, col.ColumnName))
                    cols = cols.AppendString(", ") + col.ColumnName;
            }
            if (cols.IsNull())
            {
                List<string> l = SchemaChecker.GetTableColumns(tableName);
                foreach (string s in l)
                    cols = cols.AppendString(", ") + s;
            }
            return str + cols + " FROM " + tableName;
        }
        
        private void UpdateRelations(DataRelation rel)
        {
            DataRow[] rows = rel.ChildTable.Select(null, null, DataViewRowState.Deleted);
            foreach (DataRow r in rows)
            {
                if (r.GetParentRow(rel, DataRowVersion.Original) == null ||
                    r.GetParentRow(rel, DataRowVersion.Original).RowState == DataRowState.Deleted)
                    r.AcceptChanges();
            }
            DataSet ds = rel.DataSet;
            if (ds != null)
            {
                foreach (DataRelation r in ds.Relations)
                {
                    if (r.ParentTable == rel.ChildTable)
                    {
                        UpdateRelations(r);
                    }
                }
            }
        }

        private void PrepareCommands(DbDataAdapter ad, DataTable dt, string dbName)
        {
            DbCommandBuilder builder = CreateCommandBuilder(ad);
            builder.SetAllValues = true;
            ad.InsertCommand = builder.GetInsertCommand();
            ad.DeleteCommand = builder.GetDeleteCommand();
            ad.UpdateCommand = builder.GetUpdateCommand();

            List<DataColumn> pk = new List<DataColumn>();

            if (dt.PrimaryKey.Length > 0)
            {
                bool ok = true;
                foreach (DataColumn c in dt.PrimaryKey)
                {
                    if (!SchemaChecker.ColumnExists(dbName, c.ColumnName))
                    {
                        ok = false;
                        break;
                    }
                }
                if (ok)
                {
                    pk.AddRange(dt.PrimaryKey);
                }
            }
            if (pk.Count == 0)
            {
                foreach (DataColumn c in dt.Columns)
                {
                    if (c.AutoIncrement && SchemaChecker.ColumnExists(dbName, c.ColumnName))
                    {
                        pk.Add(c);
                        break;
                    }
                }
            }
            DataTable _dbSchemaTable = builder.GetFieldValue("_dbSchemaTable") as DataTable;

            if (pk.Count == 0)
            {
                if (_dbSchemaTable != null)
                {
                    List<string> list = SchemaChecker.GetTableColumns(dbName);
                    foreach (string col in list)
                    {
                        if (dt.Columns.Contains(col) && _dbSchemaTable.RowExists("ColumnName = " + col.EscapeSql() + " AND IsUnique = 1"))
                        {
                            pk.Add(dt.Columns[col]);
                            break;
                        }
                    }
                }
            }
            if (pk.Count > 0)
            {
                ad.DeleteCommand.CommandText = ad.DeleteCommand.CommandText.XReplace(" WHERE .*", "");
                ad.UpdateCommand.CommandText = ad.UpdateCommand.CommandText.XReplace(" WHERE .*", "");
                List<DbParameter> par = new List<DbParameter>();
                foreach (DbParameter p in ad.DeleteCommand.Parameters)
                {
                    if (!ad.DeleteCommand.CommandText.Contains(p.ParameterName))
                    {
                        par.Add(p);
                    }
                }
                foreach (DbParameter p in par)
                    ad.DeleteCommand.Parameters.Remove(p);

                par.Clear();
                foreach (DbParameter p in ad.UpdateCommand.Parameters)
                {
                    if (!ad.UpdateCommand.CommandText.Contains(p.ParameterName))
                    {
                        par.Add(p);
                    }
                }
                foreach (DbParameter p in par)
                    ad.UpdateCommand.Parameters.Remove(p);

                StringBuilder sbuilder = new StringBuilder();
                string str1 = string.Empty;
                ad.DeleteCommand.CommandText += " WHERE ";
                ad.UpdateCommand.CommandText += " WHERE ";
                if (_dbSchemaTable != null)
                {
                    foreach (DataColumn c in pk)
                    {
                        ad.DeleteCommand.CommandText += str1;
                        ad.UpdateCommand.CommandText += str1;

                        str1 = " AND ";

                        DataRow colRow = _dbSchemaTable.SelectFirstRow("ColumnName = " + c.ColumnName.EscapeSql());
                        if (colRow != null)
                        {
                            DbParameter p = CreateParameter("@p" + (ad.DeleteCommand.Parameters.Count + 1), (object)null);
                            DbParameter p2 = CreateParameter("@p" + (ad.UpdateCommand.Parameters.Count + 1), (object)null);

                            p.SourceColumn = c.ColumnName;
                            p2.SourceColumn = c.ColumnName;
                            p.SourceVersion = DataRowVersion.Original;
                            p2.SourceVersion = DataRowVersion.Original;

                            builder.RunMethod("ApplyParameterInfo", p, colRow, StatementType.Delete, true);
                            builder.RunMethod("ApplyParameterInfo", p2, colRow, StatementType.Update, true);

                            ad.DeleteCommand.Parameters.Add(p);
                            ad.UpdateCommand.Parameters.Add(p2);

                            ad.DeleteCommand.CommandText += "( " + c.ColumnName + " = @p" + ad.DeleteCommand.Parameters.Count + " )"; ;
                            ad.UpdateCommand.CommandText += "( " + c.ColumnName + " = @p" + ad.UpdateCommand.Parameters.Count + " )";

                        }
                    }
                    ad.DeleteCommand.CommandText += sbuilder.ToString();
                    ad.UpdateCommand.CommandText += sbuilder.ToString();
                    DbCommand cmd = ad.DeleteCommand;
                    ad.DeleteCommand = cmd.Connection.CreateCommand();
                    ad.DeleteCommand.CommandText = cmd.CommandText;
                    DbParameter[] parameters = new DbParameter[cmd.Parameters.Count];
                    cmd.Parameters.CopyTo(parameters, 0);
                    foreach (DbParameter p in parameters)
                    {
                        cmd.Parameters.Remove(p);
                        ad.DeleteCommand.Parameters.Add(p);
                    }

                    cmd = ad.UpdateCommand;
                    ad.UpdateCommand = cmd.Connection.CreateCommand();
                    ad.UpdateCommand.CommandText = cmd.CommandText;
                    parameters = new DbParameter[cmd.Parameters.Count];
                    cmd.Parameters.CopyTo(parameters, 0);
                    foreach (DbParameter p in parameters)
                    {
                        cmd.Parameters.Remove(p);
                        ad.UpdateCommand.Parameters.Add(p);
                    }
                }
            }
        }
        /// <summary>
        /// Disposes current instance
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (transaction != null)
                {
                    try { transaction.Dispose(); }catch{ }
                }
                try
                {
                    foreach (DbDataAdapter ad in adapters.Values)
                        ad.Dispose();
                    if (fillAdapter != null)
                        fillAdapter.Dispose();
                }
                catch { }
            }
        }
    }
    /// <summary>
    /// Rows Update Results
    /// </summary>
    public enum RowsUpdateResults
    {
        /// <summary>
        /// All rows ware updated succesfully
        /// </summary>
        AllSucceeded,
        /// <summary>
        /// Not all rows ware updated succesfully
        /// </summary>
        NotAllSucceeded,
        /// <summary>
        /// None row was updated succcesfully
        /// </summary>
        AllFailed,
    }
    /// <summary>
    /// Base class to create DataManager
    /// </summary>
    public abstract class DataManagerCreator
    {
        /// <summary>
        /// Creates DataManager
        /// </summary>
        /// <returns></returns>
        public abstract DataManager CreateManager();
    }
}
